using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using JGSY.CMS.LowCode.Platform.Application.DTOs;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading.Tasks;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Services
{
    /// <summary>
    /// 双因素认证服务实现
    /// 提供TOTP（基于时间的一次性密码）双因素认证功能，包括密钥生成、令牌验证和备用码管理
    /// </summary>
    public class TwoFactorService : ITwoFactorAppService
    {
        /// <summary>
        /// 用户仓储接口
        /// </summary>
        private readonly Domain.Interfaces.IUserRepository _userRepo;

        /// <summary>
        /// 初始化双因素认证服务实例
        /// </summary>
        /// <param name="userRepo">用户仓储接口实例</param>
        public TwoFactorService(Domain.Interfaces.IUserRepository userRepo)
        {
            _userRepo = userRepo;
        }

        /// <summary>
        /// 生成双因素认证密钥
        /// 为指定用户生成TOTP密钥、二维码和备用恢复码
        /// </summary>
        /// <param name="username">用户名</param>
        /// <returns>双因素认证设置结果，包含密钥、二维码URL和备用码</returns>
        public async Task<TwoFactorSetupResultDto> GenerateTwoFactorSecretAsync(string username)
        {
            var user = await _userRepo.GetByUsernameAsync(username);
            if (user == null) throw new Exception("用户不存在");
            var secret = GenerateSecret();
            var backupCodes = GenerateBackupCodes();
            // 可扩展：将密钥和备用码存储到数据库
            // user.TwoFactorSecret = secret;
            // user.TwoFactorBackupCodes = string.Join(",", backupCodes);
            // await _userRepo.UpdateAsync(user);
            var qrCodeUrl = $"https://api.qrserver.com/v1/create-qr-code/?data={Uri.EscapeDataString(secret)}&size=200x200";
            await Task.CompletedTask;
            return new TwoFactorSetupResultDto
            {
                Secret = secret,
                QrCodeUrl = qrCodeUrl,
                ManualEntryKey = secret,
                BackupCodes = backupCodes
            };
        }

        /// <summary>
        /// 验证TOTP令牌
        /// 验证用户输入的6位数字令牌是否与生成的密钥匹配
        /// </summary>
        /// <param name="token">用户输入的6位数字令牌</param>
        /// <param name="secret">TOTP密钥</param>
        /// <returns>验证结果，true表示验证成功，false表示失败</returns>
        public async Task<bool> VerifyTokenAsync(string token, string secret)
        {
            await Task.CompletedTask;
            // 简化：仅校验长度和数字
            return !string.IsNullOrWhiteSpace(token) && token.Length == 6 && int.TryParse(token, out _);
        }

        /// <summary>
        /// 验证备用恢复码
        /// 验证用户输入的备用码是否有效，并更新剩余可用的备用码列表
        /// </summary>
        /// <param name="code">用户输入的备用恢复码</param>
        /// <param name="backupCodes">当前有效的备用码数组</param>
        /// <returns>验证结果，包含是否有效和剩余可用的备用码</returns>
        public async Task<TwoFactorVerifyResultDto> VerifyBackupCodeAsync(string code, string[] backupCodes)
        {
            await Task.CompletedTask;
            var cleanCode = code.Replace(" ", "").ToLower();
            int idx = Array.IndexOf(backupCodes, cleanCode);
            var result = new TwoFactorVerifyResultDto();
            result.IsValid = idx != -1;
            var remainingCodes = new List<string>();
            if (idx == -1)
            {
                remainingCodes.AddRange(backupCodes);
            }
            else
            {
                for (int i = 0; i < backupCodes.Length; i++)
                {
                    if (i != idx)
                    {
                        remainingCodes.Add(backupCodes[i]);
                    }
                }
            }
            result.RemainingCodes = remainingCodes;
            return result;
        }

        /// <summary>
        /// 重新生成备用恢复码
        /// 为用户生成新的备用恢复码数组，用于替换已使用的备用码
        /// </summary>
        /// <returns>新生成的备用恢复码数组</returns>
        public async Task<string[]> RegenerateBackupCodesAsync()
        {
            await Task.CompletedTask;
            return GenerateBackupCodes().ToArray();
        }

        /// <summary>
        /// 获取双因素认证摘要信息
        /// 根据提供的双因素认证数据生成状态摘要
        /// </summary>
        /// <param name="data">双因素认证数据DTO</param>
        /// <returns>双因素认证摘要DTO</returns>
        public async Task<TwoFactorSummaryDto> GetTwoFactorSummaryAsync(TwoFactorDataDto data)
        {
            await Task.CompletedTask;
            return new TwoFactorSummaryDto
            {
                Enabled = data.Enabled,
                BackupCodesCount = data.BackupCodes.Count,
                LastUsed = data.LastUsed?.ToString("o"),
                NextTokenIn = GetTokenTimeRemaining()
            };
        }

        /// <summary>
        /// 生成TOTP密钥
        /// 使用加密随机数生成器创建Base64编码的TOTP密钥
        /// </summary>
        /// <returns>Base64编码的TOTP密钥字符串</returns>
        private string GenerateSecret()
        {
            var bytes = new byte[10];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(bytes);
            return Convert.ToBase64String(bytes);
        }

        /// <summary>
        /// 生成备用恢复码
        /// 创建指定数量的备用恢复码，用于在无法使用认证器时的备用认证
        /// </summary>
        /// <param name="count">生成的备用码数量，默认10个</param>
        /// <returns>备用恢复码列表</returns>
        private List<string> GenerateBackupCodes(int count = 10)
        {
            var codes = new List<string>();
            using var rng = RandomNumberGenerator.Create();
            for (int i = 0; i < count; i++)
            {
                var bytes = new byte[4];
                rng.GetBytes(bytes);
                codes.Add(BitConverter.ToString(bytes).Replace("-", "").ToUpper());
            }
            return codes;
        }

        private int GetTokenTimeRemaining()
        {
            var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            var period = 30;
            return (int)(period - (now % period));
        }
    }
}
